package dbCore

import (
	"errors"
	"fmt"
	"github.com/roseduan/mmap-go"
	"io/ioutil"
	"os"
	"sort"
	"strconv"
	"strings"
)

const (
	// FilePerm 默认的创建文件权限
	FilePerm = 0644

	// DBFileFormatName 默认数据文件名称格式化
	DBFileFormatName = "%09d.data"

	PathSeparator = string(os.PathSeparator)
)

// FileRWMethod 文件数据读写的方式
//type FileRWMethod uint8

type DBfile struct {
	Id           uint32
	path         string
	File         *os.File
	mmap         mmap.MMap // 该文件在内存中的映射
	Offset       int64
	fileRWMethod int //文件数据读写的方式
}

// Build 加载数据文件
func Build(path string, method int, blockSize int64) (map[uint32]*DBfile, uint32, error) {
	dir, err := ioutil.ReadDir(path)
	if err != nil {
		return nil, 0, err
	}

	var fileIds []int
	for _, d := range dir {
		if strings.HasSuffix(d.Name(), "data") {
			splitNames := strings.Split(d.Name(), ".")
			id, _ := strconv.Atoi(splitNames[0])
			fileIds = append(fileIds, id)
		}
	}

	sort.Ints(fileIds)
	var activeFileId uint32 = 0
	archFiles := make(map[uint32]*DBfile)
	if len(fileIds) > 0 {
		activeFileId = uint32(fileIds[len(fileIds)-1])

		for i := 0; i < len(fileIds)-1; i++ {
			id := fileIds[i]

			file, err := NewDBFile(path, uint32(id), method, blockSize)
			if err != nil {
				return nil, activeFileId, err
			}

			archFiles[uint32(id)] = file
		}
	}

	return archFiles, activeFileId, nil
}

// NewDBFile 新建一个数据读写文件，如果是MMap，则需要Truncate文件并进行加载
func NewDBFile(path string, fileId uint32, fileRWMethod int, blockSize int64) (*DBfile, error) {
	filePath := path + PathSeparator + fmt.Sprintf(DBFileFormatName, fileId)

	file, err := os.OpenFile(filePath, os.O_CREATE|os.O_RDWR, FilePerm)
	if err != nil {
		return nil, err
	}

	df := &DBfile{Id: fileId, path: path, Offset: 0, fileRWMethod: fileRWMethod}

	if fileRWMethod == 0 {
		df.File = file
	} else {
		if err = file.Truncate(blockSize); err != nil {
			return nil, err
		}
		if m, err := mmap.Map(file, os.O_RDWR, 0); err != nil {
			return nil, err
		} else {
			df.mmap = m
		}
	}

	return df, nil
}

// Close 读写后进行关闭操作
//sync 关闭前是否持久化数据
func (df *DBfile) Close(sync bool) (err error) {
	if sync {
		err = df.Sync()
	}

	if df.File != nil {
		err = df.File.Close()
	}
	if df.mmap != nil {
		err = df.mmap.Unmap()
	}
	return
}

// Sync 数据持久化
func (df *DBfile) Sync() (err error) {
	if df.File != nil {
		err = df.File.Sync()
	}

	if df.mmap != nil {
		err = df.mmap.Flush()
	}
	return
}

// 将entry写入到文件中
func (df *DBfile) Write(entry *Entry) error {
	if entry == nil || entry.Meta.KeySize == 0 {
		return errors.New("Entry为空或者Entry的key大小为0")
	}
	fileRWMethod := df.fileRWMethod
	offset := df.Offset
	if encVal, err := entry.Encode(); err != nil {
		return err
	} else {
		if fileRWMethod == 0 {
			if _, err := df.File.WriteAt(encVal, offset); err != nil {
				return err
			}
		}
		if fileRWMethod == 1 {

		}
	}
	df.Offset += int64(entry.Size())
	return nil
}
